#-*- coding: utf-8 -*-

import pygame
import cPickle

from common import *
from pickle import UnpicklingError, PicklingError

MAX_WALK_STATE = 3

__player_sprite_center = None
__player_center = None
    
def get_player_sprite_center():
    return __player_sprite_center

def get_player_center():
    return __player_center

class Player:
    
    POS = 1
    HP = 2
    LEVEL = 3
    EXP = 4
    POW = 5
    DEFE = 6
    
    def __init__(self, id, name, role, hp, max_hp, level, exp, pow, defe, pos):
        self.id = id
        self.name = name
        self.role = role
        self.hp = hp
        self.max_hp = max_hp
        self.level = level
        self.exp = exp
        self.pow = pow
        self.defe = defe
        self.pos = pos
        self.change_hook_func = []
        self.tag = None
        
    def __trig_hook(self, data):
        for hook_func in self.change_hook_func:
            hook_func(self, data)
        
    def add_change_hook(self, func):
        self.change_hook_func.append(func)
        
    def remove_change_hook(self, func):
        for hook_func in self.change_hook_func:
            if func == hook_func:
                self.change_hook_func.remove(hook_func)
                break
        
    def walkto(self, pos):
        if pos == self.pos:
            return
        self.pos = pos
        data = {}
        data[self.POS] = self.pos
        self.__trig_hook(data)
            
    def add_hp(self, val):
        new_hp = self.hp + val
        if new_hp < 0:
            self.hp = 0
        elif new_hp > self.max_hp:
            self.hp = self.max_hp
        else:
            self.hp = new_hp
            
        data = {}
        data[self.HP] = self.hp
        self.__trig_hook(data)
        
    def add_exp(self, val):
        self.exp += self.val
        data = {}
        data[self.EXP] = self.exp 
        
        #升级

        self.__trig_hook(data)
        
    def set_tag(self, tag):
        self.tag = tag
        
    def get_tag(self):
        return self.tag
        

class PlayerChangeHooker:
    def __init__(self):
#        self.player_center = player_center
        self.change_list = {}
        self.threhold = 2000
        self.last_save_tick = pygame.time.get_ticks()
        
    def __call__(self, player, data):
        if not player.id in self.change_list:
            self.change_list[player.id] = []
        self.change_list[player.id].append( (player,data) )
            
    def update(self):
        current_cick = pygame.time.get_ticks()
        if current_cick - self.last_save_tick < self.threhold:
            return
        self.last_save_tick = current_cick
        player_center = get_player_center()
        for id in self.change_list.keys():
            player_center.save_player(id)
            
            
class PlayerCenter:
    
    __player_data_dir = 'users'
    
    def __init__(self, server_host):
        self.server_host = server_host
        self.players = {}
        self.player_name2id = {}
        self.id2hid = {}
        self.online_players = {}
        self.hooker = PlayerChangeHooker()
        self.start_id = 1
        self.__load_players()
        
    def __get_id(self, filename):
        index = filename.find('.u')
        if index == -1:
            return -1
        return int(filename[:index])
    
    def __get_filename(self, id):
        return str(id) + '.u'
    
    def __create_player(self, name, role):
        id = self.start_id
        player = Player(id, name, role, 100, 100, 1, 0, 10, 2, (0,0))
        if not self.__save_player(player):
            return None
        self.start_id += 1
        self.__add_player(player)
        return player
    
    def __load_players(self):
        if not os.path.exists(self.__player_data_dir):
            os.mkdir(self.__player_data_dir)
        for each_player_file in os.listdir(self.__player_data_dir):
            player_id = self.__get_id(each_player_file)
            if player_id == -1:
                continue
            try:
                pf = open(each_player_file, 'r')
                player = cPickle.load(pf)
                self.__add_player(player)
                if player.id >= self.start_id:
                    self.start_id = player.id + 1
            except (IOError, UnpicklingError):
                continue
        
    def __add_player(self, player):
        self.players[player.id] = player
        self.player_name2id[player.name] = player.id
        player.add_change_hook(self.hooker)
        
    def __remove_player(self, player):
        try:
            self.players.pop(player.id)
        except KeyError:
            pass            
        player.remove_change_hook(self.hooker)
        
    def online_ids(self):
        return self.online_players.keys()
    
    def get_hid(self, id):
        if id in self.id2hid:
            return self.id2hid[id]
        return None
        
    def is_online(self, id):
        return id in self.online_players
        
    def update(self):
        self.hooker.update()
        
    def save_player(self, id):
        if not id in self.players:
            return False
        
        player = self.players[id]
        self.__save_player(player)
        
        return True
    
    def __save_player(self, player):
        id = player.id
        filename = os.path.join(self.__player_data_dir, self.__get_filename(id))
        try:
            pf = open(filename, 'wb+')
            cPickle.dump(player, pf, protocol=0)
        except (IOError, PicklingError):
            return False
        finally:
            pf.close()
        return True
    
    def save_all(self):
        for id in self.players:
            self.save_player(id)
        
    def login(self, name, hid):
        
        if not name in self.player_name2id:
            return None
        
        id = self.player_name2id[name]
        if not id in self.players:
            return None
        
        #如果已经登录，则将原来登录过的用户踢掉
        if id in self.online_players:
            self.logout(id)
            
        player = self.players[id]
        self.online_players[id] = player
        self.id2hid[id] = hid
        player.set_tag(hid)
        
        return player
    
    def logout(self, id):
        if not self.is_online(id):
            return None
        player = self.online_players[id]
        
        #关闭网络连接
        hid = player.get_tag()
        if hid:
            self.server_host.close(hid)
            player.set_tag(None)
        
        self.online_players.pop(id)
        self.id2hid.pop(id)
        return player
    
    def reg(self, name, role):
        if name in self.player_name2id:
            return None
        player = self.__create_player(name, role)        
        return player
        
        
    def find_player(self, id):
        if id in self.players:
            return self.players[id]
        return None
    
class PlayerSprite(pygame.sprite.Sprite):
    groups = []
    images = {
              DIR_NORTH : [],
              DIR_SOUTH : [],
              DIR_WEST : [],
              DIR_EAST : []
              }
    def __init__(self, id, hp, pos):
        pygame.sprite.Sprite.__init__(self, self.groups)
        self.id = id
        self.hp = hp
        self.pos = pos
        self.dir = DIR_SOUTH
        self.w_state = 0
        self.image = self.images[self.dir][self.w_state]
        self.rect = self.image.get_rect(center = self.pos)
        
    def __get_dir(self, des):
        x_d = des[0] - self.pos[0]
        y_d = des[1] - self.pos[1]
        dir = self.dir
        if x_d < 0:
            dir = DIR_WEST
        elif x_d > 0:
            dir = DIR_EAST
        elif y_d < 0:
            dir = DIR_NORTH
        elif y_d > 0:
            dir = DIR_SOUTH
        else:
            pass
        return dir
    
    def walkto(self, des):
        if self.pos[0] == des[0] and \
            self.pos[1] == des[1]:
            return 
        dir = self.__get_dir(des)
        if self.dir != dir:
            self.w_state = 0
        else:
            self.w_state = (self.w_state + 1) % MAX_WALK_STATE
        self.dir = dir
        self.pos = des
        self.image = self.images[self.dir][self.w_state]
        self.rect = self.image.get_rect(center = self.pos)
        
class PlayerSpriteCenter:
    def __init__(self, player_group):
        self.players = {}
        self.my_player = None
        self.player_group = player_group
        
    def set_my_player(self, player):
        self.my_player = player
        self.add_player(player)
    
    def get_my_player(self):
        return self.my_player
        
    def add_player(self, player):
        self.players[player.id] = player
        self.player_group.add(player, level = 1)
        
    def remove_player(self, id):
        player = self.players.pop(id)
        if not player:
            return
        self.player_group.remove(player)
        
    def find_player(self, id):
        try:
            return self.players[id]
        except KeyError:
            return None
        return None
    
def init_player_sprite():
    PlayerSprite.images[DIR_SOUTH].append(load_image('player1_1.png'))
    PlayerSprite.images[DIR_SOUTH].append(load_image('player1_2.png'))
    PlayerSprite.images[DIR_SOUTH].append(load_image('player1_3.png'))
    
    PlayerSprite.images[DIR_NORTH].append(load_image('player1_1.png'))
    PlayerSprite.images[DIR_NORTH].append(load_image('player1_2.png'))
    PlayerSprite.images[DIR_NORTH].append(load_image('player1_3.png'))
    
    PlayerSprite.images[DIR_WEST].append(load_image('player1_1.png'))
    PlayerSprite.images[DIR_WEST].append(load_image('player1_2.png'))
    PlayerSprite.images[DIR_WEST].append(load_image('player1_3.png'))
    
    PlayerSprite.images[DIR_EAST].append(load_image('player1_1.png'))
    PlayerSprite.images[DIR_EAST].append(load_image('player1_2.png'))
    PlayerSprite.images[DIR_EAST].append(load_image('player1_3.png'))
    
def init_player_sprite_center(group):
    global __player_sprite_center
    __player_sprite_center = PlayerSpriteCenter(group)
    
def init_player(server_host):
    global __player_center
    
    __player_center = PlayerCenter(server_host)